package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.constant.CacheConstants;
import com.zzyl.constant.Constants;
import com.zzyl.dto.AlertRuleDto;
import com.zzyl.entity.AlertData;
import com.zzyl.entity.AlertRule;
import com.zzyl.entity.DeviceData;
import com.zzyl.mapper.AlertDataMapper;
import com.zzyl.mapper.AlertRuleMapper;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.mapper.UserRoleMapper;
import com.zzyl.properties.AlertRuleProperties;
import com.zzyl.service.AlertRuleService;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.utils.StringUtils;
import com.zzyl.vo.AlertRuleVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * @author sjqn
 */
@Slf4j
@Service
public class AlertRuleServiceImpl implements AlertRuleService {

    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;
    
    @Autowired
    private AlertRuleProperties alertRuleProperties;

    @Autowired
    private AlertDataMapper alertDataMapper;

    /**
     * 新增报警规则
     *
     * @param alertRuleDto 报警规则请求模型
     */
    @Override
    public void createRule(AlertRuleDto alertRuleDto) {
        AlertRule alertRule = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertRuleMapper.insert(alertRule);
    }

    /**
     * 分页条件查询
     *
     * @param pageNum       页码
     * @param pageSize      页面大小
     * @param alertRuleName 报警规则名称
     * @param productKey    产品key
     * @param functionName  功能名称
     * @return 分页结果
     */
    @Override
    public PageResponse<AlertRuleVo> getAlertRulePage(Integer pageNum, Integer pageSize, String alertRuleName, String productKey, String functionName) {
        PageHelper.startPage(pageNum, pageSize);
        Page<AlertRuleVo> page = alertRuleMapper.page(alertRuleName, productKey, functionName);
        //添加提示
        page.getResult().forEach(alertRuleVo -> {
            String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警", alertRuleVo.getFunctionName(), alertRuleVo.getOperator(), alertRuleVo.getValue(), alertRuleVo.getDuration());
            alertRuleVo.setRules(alertReason);
        });

        return PageResponse.of(page, AlertRuleVo.class);
    }

    /**
     * 删除报警
     *
     * @param id 报警规则id
     */
    @Override
    public void deleteAlertRule(Long id) {
        alertRuleMapper.deleteByPrimaryKey(id);
    }

    /**
     * 修改报警
     *
     * @param id           报警规则id
     * @param alertRuleDto 报警规则更新请求模型
     */
    @Override
    public void updateAlertRule(Long id, AlertRuleDto alertRuleDto) {
        AlertRule alertRule = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertRuleMapper.updateByPrimaryKeySelective(alertRule);
    }

    /**
     * 查询报警信息
     *
     * @param id 报警规则id
     * @return 报警信息
     */
    @Override
    public AlertRuleVo readAlertRule(Long id) {
        AlertRule alertRule = alertRuleMapper.selectByPrimaryKey(id);
        return BeanUtil.toBean(alertRule, AlertRuleVo.class);
    }

    /**
     * 启用或禁用
     *
     * @param id     报警id
     * @param status 报警规则状态
     */
    @Override
    public void enableOrDisable(Long id, Integer status) {
        alertRuleMapper.updateStatus(id, status);
    }

    @Override
    public void alertFilter() {
        List<AlertRule> alertRules = alertRuleMapper.selectAll(Constants.STATUS_ENABLE);
        if (alertRules.isEmpty()) {
            return;
        }
        List<Object> values = redisTemplate.opsForHash().values(CacheConstants.DEVICE_LASTDATA_CACHE_KEY);
        if (values.isEmpty()) {
            return;
        }
        List<DeviceData> deviceDataList = new ArrayList<>();
        values.forEach(value -> {
//            DeviceData deviceData = JSONUtil.toBean(value.toString(), DeviceData.class);
            deviceDataList.addAll(JSONUtil.toList(value.toString(), DeviceData.class));
        });
        deviceDataList.forEach(this::alertFilter);
    }

    /**
     * deviceData
     * {
     *         "accessLocation": "86",
     *         "locationType": 1,
     *         "physicalLocationType": 1,
     *         "deviceDescription": "404,86",
     *         "alarmTime": 1757939288730,
     *         "dataValue": "66",
     *         "deviceName": "ywbj_yxm_01",
     *         "functionId": "CurrentHumidity",
     *         "iotId": "HegA51CkWG4bQoTSWwmgk1mp00",
     *         "nickname": "ywbj_yxm_01",
     *         "productKey": "k1mp05UoEeN",
     *         "productName": "烟雾报警器_yangxiangmin",
     *         "id": 1115917,
     *         "createTime": 1757939288846,
     *         "updateTime": 1757939288846,
     *         "createBy": 1,
     *         "params": {}
     * <p>
     * create table alert_rule
     * (
     *     id                     bigint auto_increment
     *         primary key,
     *     product_key            varchar(50)                  null comment '所属产品的key',
     *     product_name           varchar(500)                 null comment '产品名称',
     *     module_id              varchar(50)                  null comment '模块的key',
     *     module_name            varchar(500)                 null comment '模块名称',
     *     function_name          varchar(255)                 null comment '功能名称',
     *     function_id            varchar(255)                 null comment '功能标识',
     *     iot_id                 varchar(255)                 null comment '物联网设备id',
     *     device_name            varchar(255)                 null comment '设备名称',
     *     alert_data_type        int                          null comment '报警数据类型，0：老人异常数据，1：设备异常数据',
     *     alert_rule_name        varchar(255)                 null comment '告警规则名称',
     *     operator               varchar(255)                 null comment '运算符',
     *     value                  float                        null comment '阈值',
     *     duration               int                          null comment '持续周期',
     *     alert_effective_period varchar(255)                 null comment '报警生效时段',
     *     alert_silent_period    int                          null comment '报警沉默周期',
     *     status                 int                          null comment '0 禁用 1启用',
     *  </p>
     */

    private void alertFilter(DeviceData deviceData) {
        long between = LocalDateTimeUtil.between(deviceData.getAlarmTime(), LocalDateTime.now(), ChronoUnit.SECONDS);
        if (between > 60) {
            return;
        }

        List<AlertRule> alertRules = alertRuleMapper.selectAllByStatusAndFunctionIdAndProductKeyAndIotId(
                Constants.STATUS_ENABLE,
                deviceData.getProductKey(),
                "-1",
                deviceData.getFunctionId()
                );
        List<AlertRule> iotIdRules = alertRuleMapper.selectAllByStatusAndFunctionIdAndProductKeyAndIotId(
                Constants.STATUS_ENABLE,
                deviceData.getProductKey(),
                deviceData.getIotId(),
                deviceData.getFunctionId()
                );

        Collection<AlertRule> alertRuleList = CollUtil.addAll(alertRules, iotIdRules);
        if (alertRuleList.isEmpty()) {
            return;
        }

        alertRuleList.forEach(alertRule -> deviceDataAndRuleHandler(deviceData, alertRule));
    }

    private void deviceDataAndRuleHandler(DeviceData deviceData, AlertRule alertRule) {
        String[] splitTime = alertRule.getAlertEffectivePeriod().split("~");
        LocalTime startTime = LocalTime.parse(splitTime[0]);
        LocalTime endTime = LocalTime.parse(splitTime[1]);
        LocalTime localTime = deviceData.getAlarmTime().toLocalTime();

        if (localTime.isBefore(startTime) || localTime.isAfter(endTime)) {
            log.info("当前时间不在生效时间段内");
            return;
        }

        Double deviceDataValue = Double.valueOf(deviceData.getDataValue());
        Double alertRuleValue = Double.valueOf(alertRule.getValue());
        int compiler = NumberUtil.compare(deviceDataValue, alertRuleValue);

        String iotId = deviceData.getIotId();
        //统计次数的key
        String aggCountKey = CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + iotId + ":" + deviceData.getFunctionId() + ":" + alertRule.getId();

        if (">=".equals(alertRule.getOperator()) && compiler >= 0 || "<".equals(alertRule.getOperator()) && compiler < 0) {
            log.info("当前数据符合报警规则");
        } else {
            //正常的数据
            log.info("当前数据不符合报警规则");
            redisTemplate.delete(aggCountKey);
            return;
        }
        //异常的数据会走到这里
        //判断是否在沉默周期内
        String silentKey = CacheConstants.ALERT_SILENT_PREFIX + iotId + ":" + deviceData.getFunctionId() + ":" + alertRule.getId();
        String silentData = redisTemplate.opsForValue().get(silentKey);
        if (StringUtils.isNotEmpty(silentData)) {
            return;
        }
        //持续周期的逻辑
        String aggData = redisTemplate.opsForValue().get(aggCountKey);
        int count = StringUtils.isEmpty(aggData) ? 1 : Integer.parseInt(aggData) + 1;
        //如果count与持续周期的值相等，则触发报警
        if (ObjectUtil.notEqual(count, alertRule.getDuration())) {
            //不相等
            redisTemplate.opsForValue().set(aggCountKey, count + "");
            return;
        }
        //删除redis的报警数据
//        redisTemplate.delete(aggCountKey);
        //存储数据到沉默周期，设置一个过期时间，规则中的沉默周期
        redisTemplate.opsForValue().set(silentKey, "1", alertRule.getAlertSilentPeriod(), TimeUnit.MINUTES);

        // 保存异常数据
        List<Long> userIds = new ArrayList<>();
        if (alertRule.getAlertDataType().equals(0)) {
            // 老人异常
            if (deviceData.getLocationType().equals(0)) {
                userIds = deviceMapper.selectNursingIdsByIotIdWithElder(deviceData.getIotId());
            } else if (deviceData.getLocationType().equals(1) && deviceData.getPhysicalLocationType().equals(2)) {
                userIds = deviceMapper.selectNursingIdsByIotIdWithBed(deviceData.getIotId());
            }
//            else if (deviceData.getLocationType().equals(1) && deviceData.getPhysicalLocationType().equals(1)) {
//                deviceMapper.selectNursingIdsByIotIdWithRoom(deviceData.getIotId());
//            }
        } else {
            // 设备异常
            userIds = userRoleMapper.selectUserIdByRoleName(alertRuleProperties.getDeviceMaintainerRole());
        }
        List<Long> managerIds = userRoleMapper.selectUserIdByRoleName(alertRuleProperties.getManagerRole());

        Collection<Long> allUserIds = CollUtil.addAll(userIds, managerIds);
        allUserIds = CollUtil.distinct(allUserIds);
        batchInsertAlertData(deviceData, allUserIds, alertRule);
    }

    private void batchInsertAlertData(DeviceData deviceData, Collection<Long> allUserIds, AlertRule alertRule) {
        AlertData alertData = BeanUtil.toBean(deviceData, AlertData.class);
        alertData.setAlertRuleId(alertRule.getId());

        String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警",
                alertRule.getFunctionName(),
                alertRule.getOperator(),
                alertRule.getValue(),
                alertRule.getDuration()
        );
        alertData.setAlertReason(alertReason);
        alertData.setStatus(0);
        alertData.setType(alertRule.getAlertDataType());

        List<AlertData> list = allUserIds.stream().map(userId -> {
            AlertData dbAlertData = BeanUtil.toBean(alertData, AlertData.class);
            dbAlertData.setUserId(userId);
            dbAlertData.setId(null);
            return dbAlertData;
        }).collect(Collectors.toList());

        log.info("批量保存异常数据:{}", list);
        alertDataMapper.batchInsert(list);
    }


}
